//
//  LYBluetoothTaskManager.swift
//  RxSwift_Practice
//
//  Created by 李永彬 on 2020/5/14.
//  Copyright © 2020 李永彬. All rights reserved.
//

import Foundation
import UIKit

protocol LYBluetoothTaskDelegate: NSObject {
    func taskFailedAndRequestRetry(task:LYBluetoothTask)
}

internal enum LYBluetoothTaskType:Int {
    case LYBluetoothTaskTypeData
    case LYBluetoothTaskTypeResponse
    case LYBluetoothTaskTypeAddressBook
}

class LYBluetoothTask:NSObject, LYBluetoothDataDelegate {

    internal enum LYBluetoothTaskState:Int {
        case LYBluetoothTaskStateNew
        case LYBluetoothTaskStateExcuting
        case LYBluetoothTaskStateFailed
        case LYBluetoothTaskStateTimeout
        case LYBluetoothTaskStateSuccess
        case LYBluetoothTaskStateCancel
    }
    
    var timeout: TimeInterval = 0
    var delegate: LYBluetoothTaskDelegate?
    var type: LYBluetoothTaskType?
    var state: LYBluetoothTaskState?
    var errorCode: LYBCCode?
    var dataModel: LYBluetoothData?
    var sendDevice: LYRemotePeripheral?
    var callback: LYBCRequestCallback?
    var btn: UIButton?
    var ob: NSKeyValueObservation!
    
    init(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) {
        super.init()
        self.dataModel = dataModel
        self.sendDevice = sendDevice
        self.dataModel?.delegate = self
        self.callback = callback

        ob = self.dataModel?.observe(\.state, options: [.new, .old], changeHandler: { (model, value) in
            print(model, value)
            switch self.dataModel!.state {
            case .LYBluetoothDataStatePlain:
                self.dataStatePlain()
                
            case .LYBluetoothDataStateSending:
                self.dataStateSending()
                
            case .LYBluetoothDataStateSent:
                self.dataStateSent()
                
            case .LYBluetoothDataStateSendFailed:
                self.dataStateSendFailed()
                
            case .LYBluetoothDataStateReciving:
                self.dataStateReciving()
                
            case .LYBluetoothDataStateRecived:
                self.dataStateRecived()
                
            case .LYBluetoothDataStateReciveFailed:
                self.dataStateReciveFailed()
                
            case .LYBluetoothDataStateTimeout:
                self.dataStateTimeout()
                
            default:
                break
            }
        })
    }
    
    deinit {
        
        self.dataModel = nil
    }
    
    func start() {

    }
    
    func cancel() {
        
    }
    
    func dataStatePlain() {
        
    }
    
    func dataStateSending() {
        
    }
    
    func dataStateSent() {
        
    }
    
    func dataStateSendFailed() {
        
    }
    
    func dataStateReciving() {
        
    }
    
    func dataStateRecived() {
        
    }
    
    func dataStateReciveFailed() {
        
    }
    
    func dataStateTimeout() {
        
    }
    
    func refreshTimeout() {
        
    }
    
    // MARK: LYBluetoothDataDelegate
    func bluetoothDataNeedRefreshTimeout() {
        self.refreshTimeout()
    }
}

class LYBluetoothDataTask: LYBluetoothTask {
    var timeoutTimer : Timer?
    
    override init(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) {
        super.init(dataModel: dataModel, sendDevice: sendDevice, callback: callback)
        self.type = .LYBluetoothTaskTypeData
    }
    
    static func taskWithDataModelAndDevice(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) -> LYBluetoothDataTask {
        let task = LYBluetoothDataTask(dataModel: dataModel, sendDevice: sendDevice, callback: callback)
        return task;
    }
    
    func removeTimer() {
        guard self.timeoutTimer != nil else {
            return
        }
        self.timeoutTimer?.invalidate()
        self.timeoutTimer = nil
    }
    
    override func start() {
        super.start()
        self.removeTimer()
        
        guard self.sendDevice != nil else {
            var code: LYBCCode = .LYBCCodeBLEDisconnected
            
            if LYCentral.shared.state == .LYBluetoothStateDisable {
                code = .LYBCCodeBLEUnavailable
            }
            self.dataModel?.errorCode = code
            self.dataModel?.state = .LYBluetoothDataStateSendFailed
            return
        }
        self.addTimeoutTimer()
        self.state = .LYBluetoothTaskStateExcuting
        self.sendDevice?.writeValue(dataModel: self.dataModel!, channel: .LYBDChannel8001)
    }
    
    override func cancel() {
        self.state = .LYBluetoothTaskStateCancel
        self.removeTimer()
    }
    
    func finish() {
        self.removeTimer()
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "LYBluetoothTaskFinishExcutingNotfication"), object: nil, userInfo: ["task" : self])
    }
    
    override func refreshTimeout() {
        self.removeTimer()
        self.addTimeoutTimer()
    }
    
    func addTimeoutTimer() {
        if self.timeout > 0 {
            self.addTimerWithTimeout(timeout: self.timeout)
        } else {
            self.addTimerWithTimeout(timeout: self.dataModel?.timeout ?? 0)
        }
    }
    
    func addTimerWithTimeout(timeout: TimeInterval) {
        self.timeoutTimer = Timer(timeInterval: timeout, target: self, selector: #selector(timeoutCallback), userInfo: nil, repeats: false)
        self.timeoutTimer?.fireDate.addTimeInterval(timeout)
        RunLoop.current.add(self.timeoutTimer!, forMode: .common)
    }
    
    @objc func timeoutCallback() {
        self.dataModel?.state = .LYBluetoothDataStateTimeout
    }
    
    // MARK: KVO
    override func dataStateSendFailed() {
        super.dataStateSendFailed()
        if self.dataModel?.errorCode == .LYBCCodeBLEUnavailable {
            self.errorCode = .LYBCCodeBLEUnavailable
        } else if self.dataModel?.errorCode == .LYBCCodeBLEDisconnected {
            if LYCentral.shared.state == .LYBluetoothStateDisable {
                self.errorCode = .LYBCCodeBLEUnavailable
            } else {
                
            }
            self.errorCode = .LYBCCodeBLEDisconnected
            
        } else {
            self.errorCode = .LYBCCodeRequestFailed
        }
        
        self.state = .LYBluetoothTaskStateFailed
        self.errorCode = .LYBCCodeBLEUnavailable
        self.finish()
    }
    
    override func dataStateRecived() {
        super.dataStateRecived()
        self.state = .LYBluetoothTaskStateSuccess
        self.finish()
    }
    
    override func dataStateReciveFailed() {
        super.dataStateReciveFailed()
        self.state = .LYBluetoothTaskStateFailed
        self.errorCode = .LYBCCodeRequestFailed
        self.finish()
    }
    
    override func dataStateTimeout() {
        super.dataStateTimeout()
        self.state = .LYBluetoothTaskStateFailed
        self.errorCode = .LYBCCodeRequestTimeout
        self.finish()
    }
}

class LYBluetoothResponseTask: LYBluetoothTask {
    override init(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) {
        super.init(dataModel: dataModel, sendDevice: sendDevice, callback: callback)
        self.type = .LYBluetoothTaskTypeResponse
    }
    
    static func taskWithDataModelAndDevice(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) -> LYBluetoothResponseTask {
        let task = LYBluetoothResponseTask(dataModel: dataModel, sendDevice: sendDevice, callback: callback)
        return task;
    }
}

class LYBluetoothAddressBookTask: LYBluetoothTask {
    override init(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) {
        super.init(dataModel: dataModel, sendDevice: sendDevice, callback: callback)
        self.type = .LYBluetoothTaskTypeAddressBook
    }
    
    static func taskWithDataModelAndDevice(dataModel: LYBluetoothData ,sendDevice: LYRemotePeripheral?, callback: @escaping LYBCRequestCallback) -> LYBluetoothAddressBookTask {
        let task = LYBluetoothAddressBookTask(dataModel: dataModel, sendDevice: sendDevice, callback: callback)
        return task;
    }
}

class LYBluetoothTaskManager: NSObject {
    
    var dataThread: Thread?
    var responsThread: Thread?
    var dataTaskList: [LYBluetoothDataTask]?
    var responseTaskList: [LYBluetoothResponseTask]?
    var addressBookTaskList: [LYBluetoothAddressBookTask]?
    var errorHandlerDict: [String : Any]?
    
    var currentDataTask: LYBluetoothDataTask?

    var dataTaskLock: NSLock?
    var responseTaskLock: NSLock?
    var addressTaskLock: NSLock?
    var errorHandlerLock: NSLock?
    
    override init() {
        super.init()
        
        dataTaskLock = NSLock()
        dataTaskLock?.name = "LYBluetoothTaskManager.dataTaskLock"
        
        responseTaskLock = NSLock();
        responseTaskLock?.name = "LYBluetoothTaskManager.mediaTaskLock"
        
        addressTaskLock = NSLock();
        addressTaskLock?.name = "LYBluetoothTaskManager.addressTaskLock"
        
        errorHandlerLock = NSLock();
        errorHandlerLock?.name = "LYBluetoothCenter.errorHandlerLock"
        
        errorHandlerDict = Dictionary()
        dataTaskList = []
        responseTaskList = []
        addressBookTaskList = Array()
        
        // 一直用这个线程去执行蓝牙命令操作
        dataThread = Thread(target: self, selector: #selector(runDataTask), object: nil)
        dataThread?.name = "LYBluetoothTaskManager.dataThread";
        dataThread?.start()
        
        responsThread = Thread(target: self, selector: #selector(runResponseTask), object: nil)
        responsThread?.name = "LYBluetoothTaskManager.mediaThread";
        responsThread?.start()
        NotificationCenter.default.addObserver(self, selector: #selector(taskFinish(noti:)), name: NSNotification.Name(rawValue: "LYBluetoothTaskFinishExcutingNotfication"), object: nil)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    // MARK: Thread
    @objc func runDataTask() {
        autoreleasepool {
            RunLoop.current.add(Port(), forMode: .default)
            RunLoop.current.run()
        }
    }
    
    @objc func runResponseTask() {
        autoreleasepool {
            RunLoop.current.add(Port(), forMode: .default)
            RunLoop.current.run()
        }
    }
    
    @objc func taskFinish(noti : Notification) {
        let task: LYBluetoothTask = noti.userInfo?["task"] as! LYBluetoothTask
        if task.type == .LYBluetoothTaskTypeData {
            self.dataTaskFinish(task: task as! LYBluetoothDataTask)
        }
    }
    
    func dataTaskFinish(task:LYBluetoothDataTask) {
        DispatchQueue.main.async {
            if task.callback != nil {
                task.callback!(task.dataModel, task.errorCode)
                task.callback = nil
            }
        }
        self.removeTask(task: task)
        if task == self.currentDataTask {
            self.currentDataTask = nil
        }
        //开始下一个任务
        self.beginDataTask()
    }
}

// MARK: Method

extension LYBluetoothTaskManager {
    
    func beginTask() {
        self.beginDataTask()
        self.beginAddressBookTask()
        self.beginResponseTask()
    }
    
    func beginDataTask() {
        self.perform(#selector(excuteDataTask), on: dataThread!, with: nil, waitUntilDone: false)
    }
    
    func beginAddressBookTask() {
        self.perform(#selector(excuteAddressBookTask), on: dataThread!, with: nil, waitUntilDone: false)
    }
    
    func beginResponseTask() {
        self.perform(#selector(excuteResponseTask), on: dataThread!, with: nil, waitUntilDone: false)
    }
    
    func addTask(task: LYBluetoothTask) {
        if task.type == .LYBluetoothTaskTypeData {
            dataTaskLock?.lock()
            self.dataTaskList?.append(task as! LYBluetoothDataTask)
            dataTaskLock?.unlock()
        }
        else if task.type == .LYBluetoothTaskTypeAddressBook {
            addressTaskLock?.lock()
            self.addressBookTaskList?.append(task as! LYBluetoothAddressBookTask)
            addressTaskLock?.unlock()
        }
        else {
            responseTaskLock?.lock()
            self.responseTaskList?.append(task as! LYBluetoothResponseTask)
            responseTaskLock?.unlock()
        }
    }
    
    func handBlePowerOff() {
        dataTaskLock?.lock()
        for task:LYBluetoothTask in self.dataTaskList! {
            task.errorCode = .LYBCCodeBLEUnavailable
        }
        dataTaskLock?.unlock()
        
        addressTaskLock?.lock()
        for task:LYBluetoothTask in self.addressBookTaskList! {
            task.errorCode = .LYBCCodeBLEUnavailable
        }
        addressTaskLock?.unlock()
        self.removeAllTaskOfType()
    }
    
    func removeAllTaskOfType() {
        self.removeAllTaskOfType(type: .LYBluetoothTaskTypeData)
        self.removeAllTaskOfType(type: .LYBluetoothTaskTypeResponse)
        self.removeAllTaskOfType(type: .LYBluetoothTaskTypeAddressBook)
    }
    
    func removeAllTaskOfType(type: LYBluetoothTaskType) {
        if type == .LYBluetoothTaskTypeData {
            dataTaskLock?.lock()
            for task: LYBluetoothTask in self.dataTaskList! {
                DispatchQueue.main.async {
                    task.callback!(task.dataModel, .LYBCCodeTaskCancelError)
                }
            }
            self.dataTaskList?.removeAll()
            dataTaskLock?.unlock()
        }
        else if type == .LYBluetoothTaskTypeAddressBook
        {
            addressTaskLock?.lock()
            for task: LYBluetoothTask in self.dataTaskList! {
                DispatchQueue.main.async {
                    task.callback!(task.dataModel, .LYBCCodeTaskCancelError)
                }
            }
            self.dataTaskList?.removeAll()
            addressTaskLock?.unlock()
        }
        else
        {
            responseTaskLock?.lock()
            responseTaskList?.removeAll()
            responseTaskLock?.unlock()
        }
    }
    
    func removeTask(task: LYBluetoothTask) {
        if task.type == .LYBluetoothTaskTypeData {
            let index = self.dataTaskList?.firstIndex{$0 == task}
            guard index != nil else {
                return
            }
            dataTaskLock?.lock()
            self.dataTaskList?.remove(at: index!)
            dataTaskLock?.unlock()
        }
        else if task.type == .LYBluetoothTaskTypeAddressBook {
            let index = self.addressBookTaskList?.firstIndex{$0 == task}
            guard index != nil else {
                return
            }
            addressTaskLock?.lock()
            self.addressBookTaskList?.remove(at: index!)
            addressTaskLock?.unlock()
        }
        else {
            let index = self.responseTaskList?.firstIndex{$0 == task}
            guard index != nil else {
                return
            }
            responseTaskLock?.lock()
            self.responseTaskList?.remove(at: index!)
            responseTaskLock?.unlock()
        }
    }
    
    @objc func excuteDataTask() {
        guard self.currentDataTask?.state != .LYBluetoothTaskStateExcuting else {
            return
        }
        if self.currentDataTask != nil && self.currentDataTask?.state == .LYBluetoothTaskStateNew {
            self.currentDataTask?.start()
        }
        else
        {
            self.currentDataTask = self.getDataTask()
            guard self.currentDataTask != nil else {
                return
            }
            self.currentDataTask?.start()
        }
    }
    
    @objc func excuteAddressBookTask() {
        
    }
    
    @objc func excuteResponseTask() {
        
    }
    
    func getDataTask() -> LYBluetoothDataTask? {
        var task: LYBluetoothDataTask?
        dataTaskLock?.lock()
        task = self.dataTaskList?.first
        dataTaskLock?.unlock()
        return task ?? nil
    }
}
